/**
 * 基于Promise的并发控制
 * 设计一个函数，可以限制请求的次数，同时请求结束之后，调用callback函数
 * sendRequest(requestList, limits, callback):void
 */

// 请求函数
function request(url, time = 1) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (Math.random() > 0.5) {
        resolve(url + "成功");
      } else {
        reject(url + "失败");
      }
    }, time * 1e3);
  });
}

// 并发函数-使用Promise.allSettled
/* async function sendRequest(requestList, limits, callback) {
  // 维护一个promise队列
  const promises = [];
  // 当前并发池
  const pool = new Set();

  for (const request of requestList) {
    // 开始执行前，先判断并发任务是否超过限制
    if (pool.size >= limits) {
      await Promise.race(pool).catch(err => console.error(err));
    }

    const promise = request();
    // 请求结束后，从pool里面删除
    const cb = () => {
      pool.delete(promise);
    };

    // 注册then任务
    promise.then(cb, cb);
    pool.add(promise);
    promises.push(promise);
  }

  // 等最后一个for await结束，属于最后一个await后面的微任务
  Promise.allSettled(promises).then(callback, callback);
} */

// 并发函数
function sendRequest(requestList, limits, callback) {
  // 定义执行队列，表示所有待执行的任务
  const promises = requestList.map((item, index) => ({ task: item, index }));
  // 定义开始能执行的并发数
  const concurrentNum = Math.min(limits, requestList.length);

  const results = [];
  let concurrentCount = 0; // 当前并发数

  // 启动初次执行的任务
  function runTaskNeeded() {
    let i = 0;
    while (i < concurrentNum) {
      i++;
      runTask();
    }
  }

  // 取出任务并执行
  function runTask() {
    const { task, index } = promises.shift();
    task && runner(task, index);
  }

  // 执行器，这里执行任务，同时更新当前并发数
  async function runner(task, index) {
    try {
      concurrentCount++;
      const res = await task();
      results[index] = res;
    } catch (error) {
      results[index] = error;
    } finally {
      concurrentCount--;
      picker();
    }
  }

  // 捞起下一个任务
  function picker() {
    // 任务队列里还有任务并且此时还有剩余并发数的时候执行
    if (concurrentCount < limits && promises.length > 0) {
      runTask();
    } else if (promises.length === 0 && concurrentCount === 0) {
      // 队列为空，并且请求也空了执行最后的回调函数
      callback && callback(results);
    }
  }

  // 开始执行
  runTaskNeeded();
}

// 测试
const requestList = [() => request("http1"), () => request("http2"), () => request("http3"), () => request("http4")];
sendRequest(requestList, 3, res => {
  console.log(res);
});
